package datacomm;

import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
import javax.swing.*;
import java.util.*;
import javax.swing.filechooser.FileFilter;

/**
 *
 * @author  l3whalen
 */
public class Client extends JFrame
{

    /** Creates new form Client */
    public Client()
    {
        client_name = null;
        while(client_name == null)
            client_name = JOptionPane.showInputDialog("Enter a name for this client:");

        listen_port = 16000 + new Random().nextInt(1000);
        directory = new ArrayList();

        initComponents();
        initRateFrame();

        tcp = new ClientP2PTCPControl(client_name, listen_port, "", -1, console);

        this.setTitle(client_name);
        setVisible(true);
    }

    private void initRateFrame()
    {
        rateFrame = new JFrame("Rate File");
        ratingField = new JTextField(3);
        rateButton = new JButton("Rate");

        rateFrame.setSize(300, 100);
        rateFrame.setLayout(new BorderLayout());
        rateFrame.add(new JLabel("Enter a rating (1-100):"), BorderLayout.NORTH);
        rateFrame.add(ratingField, BorderLayout.CENTER);
        rateFrame.add(rateButton, BorderLayout.SOUTH);
        rateFrame.setVisible(false);

        class rateButtonListener implements ActionListener
        {
            public void actionPerformed(ActionEvent event)
            {
                if(directoryList.getSelectedIndex() == -1)
                {
                    Globals.output("You must select an item from the list first!", true);
                    return;
                }

                if(ratingField.getText().isEmpty())
                {
                    Globals.output("Please enter a rating!", true);
                    return;
                }
                String file_name = directoryList.getSelectedValue().toString();
                int rating = Integer.parseInt(ratingField.getText());
                if(rating < 0 || rating > 100)
                {
                    Globals.output("The rating must be between 0 and 100!", true);
                }
                else
                {
                    String[] header = {file_name + ";" + rating};
                    Packet packet = new Packet();
                    packet.buildPacket(Packet.PacketType.RATE_CONTENT, header, "", server_port);
                    boolean ack = UDPSend(packet);
                    if(ack)
                    {
                        directory.get(directoryList.getSelectedIndex()).rate(rating);
                        updateDirectory();
                    }
                }
                directoryList.setSelectedIndex(-1);
                ratingField.setText("");
                rateFrame.setVisible(false);
            }
        }

        rateButton.addActionListener(new rateButtonListener());
    }
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jFrame1 = new javax.swing.JFrame();
        jFrame2 = new javax.swing.JFrame();
        jDialog1 = new javax.swing.JDialog();
        jScrollPane1 = new javax.swing.JScrollPane();
        directoryList = new javax.swing.JList();
        QueryForContent = new javax.swing.JButton();
        downloadButton = new javax.swing.JButton();
        InformAndUpdate = new javax.swing.JButton();
        searchField = new javax.swing.JTextField();
        RateContent = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        ratingList = new javax.swing.JList();
        jLabel6 = new javax.swing.JLabel();
        jLabel7 = new javax.swing.JLabel();
        Exit = new javax.swing.JButton();
        jScrollPane3 = new javax.swing.JScrollPane();
        console = new javax.swing.JTextArea();
        jLabel8 = new javax.swing.JLabel();
        jPanel1 = new javax.swing.JPanel();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        jLabel9 = new javax.swing.JLabel();
        jPanel2 = new javax.swing.JPanel();
        jLabel2 = new javax.swing.JLabel();

        javax.swing.GroupLayout jFrame1Layout = new javax.swing.GroupLayout(jFrame1.getContentPane());
        jFrame1.getContentPane().setLayout(jFrame1Layout);
        jFrame1Layout.setHorizontalGroup(
            jFrame1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        jFrame1Layout.setVerticalGroup(
            jFrame1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout jFrame2Layout = new javax.swing.GroupLayout(jFrame2.getContentPane());
        jFrame2.getContentPane().setLayout(jFrame2Layout);
        jFrame2Layout.setHorizontalGroup(
            jFrame2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        jFrame2Layout.setVerticalGroup(
            jFrame2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout jDialog1Layout = new javax.swing.GroupLayout(jDialog1.getContentPane());
        jDialog1.getContentPane().setLayout(jDialog1Layout);
        jDialog1Layout.setHorizontalGroup(
            jDialog1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        jDialog1Layout.setVerticalGroup(
            jDialog1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        jScrollPane1.setViewportView(directoryList);

        QueryForContent.setText("Search");
        QueryForContent.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                QueryForContentActionPerformed(evt);
            }
        });

        downloadButton.setText("Download");
        downloadButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                downloadButtonActionPerformed(evt);
            }
        });

        InformAndUpdate.setText("Upload Files");
        InformAndUpdate.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                InformAndUpdateActionPerformed(evt);
            }
        });

        RateContent.setText("Rate");
        RateContent.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                RateContentActionPerformed(evt);
            }
        });

        jLabel1.setText("File");

        jScrollPane2.setViewportView(ratingList);

        jLabel6.setText("Rating");

        jLabel7.setText("Search Results:");

        Exit.setText("Exit");
        Exit.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                ExitActionPerformed(evt);
            }
        });

        console.setColumns(20);
        console.setEditable(false);
        console.setRows(5);
        console.setName("Console"); // NOI18N
        jScrollPane3.setViewportView(console);

        jLabel8.setText("Console");

        jPanel1.setBorder(javax.swing.BorderFactory.createEtchedBorder(javax.swing.border.EtchedBorder.RAISED, new java.awt.Color(204, 204, 204), null));

        jLabel3.setText("Jenny Ta     500279146");

        jLabel4.setText("Lyndsie Whalen   500143650");

        jLabel5.setText("Thavisha Lokuge    500269263");

        jLabel9.setFont(new java.awt.Font("Segoe Script", 1, 18)); // NOI18N
        jLabel9.setText("PROJECT DESIGNERS");

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGap(28, 28, 28)
                        .addComponent(jLabel9))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGap(38, 38, 38)
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(jLabel5)
                            .addComponent(jLabel4))))
                .addContainerGap(33, Short.MAX_VALUE))
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
                .addContainerGap(79, Short.MAX_VALUE)
                .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(68, 68, 68))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel9)
                .addGap(18, 18, 18)
                .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel4)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel5)
                .addContainerGap(28, Short.MAX_VALUE))
        );

        jPanel2.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true));

        jLabel2.setFont(new java.awt.Font("Segoe UI", 1, 14)); // NOI18N
        jLabel2.setText("CPS706 P2P Application - Professor J. Misic");

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 333, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGap(10, 10, 10)
                        .addComponent(jLabel8))
                    .addGroup(layout.createSequentialGroup()
                        .addGap(172, 172, 172)
                        .addComponent(downloadButton, javax.swing.GroupLayout.PREFERRED_SIZE, 154, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(RateContent, javax.swing.GroupLayout.PREFERRED_SIZE, 142, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(Exit))
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                    .addGroup(layout.createSequentialGroup()
                                        .addComponent(searchField, javax.swing.GroupLayout.DEFAULT_SIZE, 284, Short.MAX_VALUE)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(QueryForContent))
                                    .addComponent(InformAndUpdate, javax.swing.GroupLayout.DEFAULT_SIZE, 355, Short.MAX_VALUE)
                                    .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                                .addGap(59, 59, 59))
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel7)
                                .addGap(333, 333, 333)))
                        .addGap(29, 29, 29)
                        .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                    .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 727, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 669, Short.MAX_VALUE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 52, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabel6))))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(layout.createSequentialGroup()
                        .addGap(21, 21, 21)
                        .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 21, Short.MAX_VALUE)
                        .addComponent(InformAndUpdate)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(searchField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(QueryForContent))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(jLabel7)
                        .addGap(5, 5, 5))
                    .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addGap(18, 18, 18)))
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel6)
                    .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane1)
                    .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(downloadButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(RateContent)
                    .addComponent(Exit))
                .addGap(3, 3, 3)
                .addComponent(jLabel8)
                .addGap(1, 1, 1)
                .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 120, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void updateDirectory()
    {
        String[] dirText = new String[directory.size()];
        String[] ratText = new String[directory.size()];
        for(int i = 0; i < directory.size(); ++i)
        {
            dirText[i] = directory.get(i).getFile();
            ratText[i] = directory.get(i).getRating() + "";
        }

        directoryList.setListData(dirText);
        ratingList.setListData(ratText);
    }

    private ReplyCode waitForAck()
    {
        debug("C: Waiting for ACK on port: " + listen_port);
        DatagramSocket ds = null;
        try
        {
            ds = new DatagramSocket(listen_port);
            byte buffer[] = new byte[Globals.BUF_SIZE];

            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            packet.setPort(server_port);
            ds.setSoTimeout(timeout_time);
            ds.receive(packet);
            ds.close();
            String str = new String(packet.getData());
            debug("C: Received ACK:\n" + str);

            return ReplyCode.REPLY_OK;
        }
        catch(SocketTimeoutException ex)
        {
            if(ds != null)
                ds.close();
            debug("C: TIMED OUT before ACK received");
            return ReplyCode.REPLY_TIMEOUT;
        }
        catch(Exception ex)
        {
            if(ds != null)
                ds.close();
            Globals.exception(ex, "C:waitForAck()");
            ex.printStackTrace();

            return ReplyCode.REPLY_ERROR;
        }
    }

    private void sendWelcomePacket()
    {
        DatagramSocket ds = null;
        try
        {
            long before = System.currentTimeMillis();
            ds = new DatagramSocket(listen_port);
            ds.send(Packet.buildClientWelcomePacket());

            byte buffer[] = new byte[Globals.BUF_SIZE];
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            ds.setSoTimeout(timeout_time);
            ds.receive(packet);
            ds.close();

            long after = System.currentTimeMillis();
            sample_rtt = (int) (after - before);
            estimated_rtt = (int) (estimated_rtt * (1-0.125) + sample_rtt * (0.125));
            dev_rtt = (int) (0.25 * Math.abs(sample_rtt - estimated_rtt));
            timeout_time = estimated_rtt + (4 * dev_rtt);
            debug("C: Estimated RTT: " + estimated_rtt);
            debug("C: Sample RTT: " + sample_rtt);
            debug("C: Dev RTT: " + dev_rtt);
            debug("C: Timeout Time: " + timeout_time);

            server_port = packet.getPort();
            debug("C: SendWelcomePacket() - Server Port: " + server_port);
        }
        catch(SocketTimeoutException ex)
        {
            if(ds != null)
                ds.close();

            debug("sendWelcomePacket(): Welcome Reply timed out!");
        }
        catch(Exception ex)
        {
            if(ds != null)
                ds.close();

            Globals.exception(ex, "C:sendWelcomePacket()");
        }
    }
    
    private boolean UDPSend(Packet packet)
    {
        if(server_port <= 0)
        {
            try
            {
                sendWelcomePacket();
                Thread.currentThread().sleep(500);
            }
            catch(Exception ex)
            {
                Globals.exception(ex, "C:UDPSend - Sleep()");
            }
        }

        DatagramSocket ds = null;
        ReplyCode code = null;
        try
        {
            for(DatagramPacket p : packet.getPackets())
            {
                code = ReplyCode.REPLY_TIMEOUT;
                int numTries = -1;
                long before = -1;
                while(code == ReplyCode.REPLY_TIMEOUT && numTries < 5)
                {
                    ++numTries;
                    if(p.getPort() != server_port)
                        p.setPort(server_port);
                    ds = new DatagramSocket();

                    debug("C: Transmitting packet:\n---\n" + new String(p.getData()) + "\n---");
                    before = System.currentTimeMillis();
                    ds.send(p);
                    ds.close();

                    code = waitForAck();
                }
                if(before > 0 && code == ReplyCode.REPLY_OK)
                {
                    long after = System.currentTimeMillis();
                    sample_rtt = (int) (after - before);
                    estimated_rtt = (int) (estimated_rtt * (1-0.125) + sample_rtt * (0.125));
                    dev_rtt = (int) ((1 - 0.25) * dev_rtt + 0.25 * Math.abs(sample_rtt - estimated_rtt));
                    timeout_time = estimated_rtt + (4 * dev_rtt);
                    
                    debug("C: Estimated RTT: " + estimated_rtt);
                    debug("C: Sample RTT: " + sample_rtt);
                    debug("C: Dev RTT: " + dev_rtt);
                    debug("C: Timeout Time: " + timeout_time);
                }
                if(code == ReplyCode.REPLY_TIMEOUT)
                {
                    Globals.error("C: Packet fully timed out:\n" + new String(p.getData()), true);
                    break;
                }
            }
            ds = new DatagramSocket();
            ds.send(Packet.buildEmptyClientPacket(Packet.PacketType.FIN, server_port));
            ds.close();
        }
        catch (Exception ex)
        {
            if(ds != null)
                ds.close();

            Globals.exception(ex, "C:UDPSend()");

            code = ReplyCode.REPLY_ERROR;
        }

        if(code == ReplyCode.REPLY_ERROR || code == ReplyCode.REPLY_TIMEOUT)
            return false;
        
        return true;
    }

    private void sendExitPacket()
    {
        Packet packet = new Packet();
        packet.buildPacket(Packet.PacketType.EXIT, null, "", server_port);
        UDPSend(packet);

        Globals.output("Push OK to exit", true);
    }
    
private void InformAndUpdateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_InformAndUpdateActionPerformed

    if(fileChooser == null)
    {
        fileChooser = new JFileChooser();
        fileChooser.setMultiSelectionEnabled(true);
        fileChooser.setFileFilter(new JPEGExtensionFilter());
    }

    try
    {
        fileChooser.setCurrentDirectory(new File("."));
        if(fileChooser.showOpenDialog(null) == JFileChooser.CANCEL_OPTION)
            return;

        File[] files = fileChooser.getSelectedFiles();

        String[] header = new String[files.length];
        for(int i = 0; i < files.length; ++i)// Spaces can cause issues, so we replace them with '&%'
            header[i] = files[i].getName().replaceAll(" ", "&%") + ";" + files[i].length();

        Packet packet = new Packet();
        packet.buildPacket(Packet.PacketType.INFORM_AND_UPDATE, header, "", server_port);
        UDPSend(packet);
    }
    catch(Exception ex)
    {
        Globals.exception(ex, "C:InformAndUpdate()");
    }
}//GEN-LAST:event_InformAndUpdateActionPerformed


private void QueryForContentActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_QueryForContentActionPerformed

    if(searchField.getText().isEmpty())
    {
        Globals.error("Server cannot be queried with an empty search. Please enter some text in the search field before searching", true);
        return;
    }

    Object[] listdata = {""};
    directoryList.setListData(listdata);
    ratingList.setListData(listdata);
    String file_name[] = {searchField.getText()};

    Packet query = new Packet();
    query.buildPacket(Packet.PacketType.QUERY_FOR_CONTENT, file_name, null, server_port);
    UDPSend(query);

    DatagramSocket ds = null;
    int num_timeouts = 0;
    try
    {
        String type = "";
        ArrayList<DatagramPacket> packets = new ArrayList<DatagramPacket>();

        do
        {
            ds = new DatagramSocket(listen_port);

            byte buffer[] = new byte[Globals.BUF_SIZE];
            DatagramPacket p = new DatagramPacket(buffer, buffer.length);
            debug("C: queryForContent: Waiting for packet");
            ds.setSoTimeout(timeout_time);
            ds.receive(p);
            ds.close();

            String data = new String(p.getData());
            String split[] = data.split(" ");
            if(split.length < 2)
            {
                Globals.error("C: queryForContent: Attempt to split invalid string");
                continue;
            }
            type = data.split(" ")[2];
            type = type.replaceAll("\n", "");
            type = type.replaceAll("\r", "");
            debug("C: Received Packet:\n---\n" + data + "\n---");
            if(!type.equalsIgnoreCase(Packet.PacketType.FIN.toString()))
            {
                packets.add(p);
                ds = new DatagramSocket();
                ds.send(Packet.buildEmptyClientPacket(Packet.PacketType.ACK, server_port));
                ds.close();
            }

            num_timeouts = 0;
        }while(!type.equalsIgnoreCase(Packet.PacketType.FIN.toString()));

        DatagramPacket packet = Packet.assemblePackets(packets);

        debug("C: Received Packet:\n---\n" + new String(packet.getData()) + "\n---");
        directory.clear();
        String header[] = new String(packet.getData()).split(Globals.CRLF);
        for(int i = 1; i < header.length; ++i)
        {
            String splat[] = header[i].split(";");
            if(splat.length < 2)
                continue;
            String file = splat[0];
            String size = splat[1];
            String address = splat[2];
            int port = Integer.parseInt(splat[3]);
            double rating = Double.parseDouble(splat[4]);
            //String file, String address, long size, int rating, int port
            directory.add(new DirectoryListEntry(file, address, size, rating, port));
        }

        updateDirectory();
    }
    catch(SocketTimeoutException ex)
    {
        if(ds != null)
            ds.close();
        num_timeouts++;
        if(num_timeouts > 5)
        {
            Globals.error("C: Packet fully timed out in query.", true);
            return;
        }
        
        debug("C: ACK for query timed out");
    }
    catch(Exception ex)
    {
        if(ds != null)
            ds.close();
        Globals.exception(ex, "C:QueryForContent()");
        return;
    }

}//GEN-LAST:event_QueryForContentActionPerformed

private void ExitActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_ExitActionPerformed
{//GEN-HEADEREND:event_ExitActionPerformed
    sendExitPacket();

    this.dispose();
}//GEN-LAST:event_ExitActionPerformed

private void formWindowClosing(java.awt.event.WindowEvent evt)//GEN-FIRST:event_formWindowClosing
{//GEN-HEADEREND:event_formWindowClosing
    sendExitPacket();

    this.dispose();
}//GEN-LAST:event_formWindowClosing

private void RateContentActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_RateContentActionPerformed
{//GEN-HEADEREND:event_RateContentActionPerformed
    rateFrame.setVisible(true);
}//GEN-LAST:event_RateContentActionPerformed

private void downloadButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_downloadButtonActionPerformed
{//GEN-HEADEREND:event_downloadButtonActionPerformed
    if(directoryList.getSelectedIndex() == -1)
    {
        Globals.error("Please select a file to be downloaded!", true);
        return;
    }
    
    if(directoryList.getSelectedIndices().length > 1)
    {
        Globals.error("You can only download one file at a time (Sorry!).\nSo please try again with only one file selected!", true);
        return;
    }

    DirectoryListEntry entry = directory.get(directoryList.getSelectedIndex());

    try
    {
        String ip = InetAddress.getLocalHost().getHostAddress();

        tcp.setTransmitInfo(entry.getAddress(), entry.getPort());
        tcp.requestFile(entry, ip, listen_port);
    }
    catch(Exception ex)
    {
        Globals.exception(ex, "C:downloadButtonActionPerformed()");
    }
}//GEN-LAST:event_downloadButtonActionPerformed

public void debug(Object object)
{
    Globals.debug(object);

    console.append(object + "\n");
}

    class JPEGExtensionFilter extends FileFilter
    {
        public boolean accept(File file)
        {
            if(file.getName().toLowerCase().endsWith(".jpeg")
                    || file.getName().toLowerCase().endsWith(".jpg"))
                return true;

            return false;
        }

        public String getDescription()
        {
            return ".jpeg and .jpg files only";
        }
    }

    private String client_name;
    
    private int listen_port = 0;
    private int server_port = 0;
    private int estimated_rtt = 100;
    private int sample_rtt = 0;
    private int dev_rtt = 0;
    private int timeout_time = 2000;
    
    private JFileChooser fileChooser;
    private ArrayList<DirectoryListEntry> directory;

    private JTextField ratingField;
    private JButton rateButton;
    private JFrame rateFrame;

    private ClientP2PTCPControl tcp;

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton Exit;
    private javax.swing.JButton InformAndUpdate;
    private javax.swing.JButton QueryForContent;
    private javax.swing.JButton RateContent;
    private javax.swing.JTextArea console;
    private javax.swing.JList directoryList;
    private javax.swing.JButton downloadButton;
    private javax.swing.JDialog jDialog1;
    private javax.swing.JFrame jFrame1;
    private javax.swing.JFrame jFrame2;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel jLabel8;
    private javax.swing.JLabel jLabel9;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JList ratingList;
    private javax.swing.JTextField searchField;
    // End of variables declaration//GEN-END:variables
}
